#include "../include/CModelManager.h"

CModelManager::CModelManager()
{
	this->m_pDevice = NULL;
}

CModelManager::~CModelManager()
{

}

//=====================================================
// Function	:	GetInstance()
//
// Out		:	CModelManager& - Handle to our single modelmanager class instance
//
//	Purpose	:	To return a reference to our model manager Class Instance
//=====================================================
CModelManager* CModelManager::GetInstance(void)
{
	static CModelManager instance;
	return &instance;
}

//=====================================================
// Function	:	Initialize()
//
// In		:	device - our d3d device
//
// Out      :   true if we initalize successfully
//
//	Purpose	:	store a local copy of our d3d device pointer
//=====================================================
bool CModelManager::Initialize(IDirect3DDevice9* device)
{
    if(!device)
        return false;
    
    m_pDevice = device;
    return true;
}

//=====================================================
// Function	:	AddModel()
//
// In			:	szFileName - The name of the model to load
//
// Out		:	int - Handle to our newly added model
//
//	Purpose	:	Create a new model and assign a unique ID to that model.
//=====================================================
int CModelManager::AddModel(const char* szFileName)
{
	char nameBuffer[56] = {0};
	sprintf_s(nameBuffer, sizeof(nameBuffer), "models/%s", szFileName);

	// First make sure that our model isnt loaded already before going too far. If so then return its ID
	// because we are done.
	for (ModelMap::iterator iter = m_Models.begin(); iter != m_Models.end(); iter++)
		if(!strcmp(szFileName, iter->second.fileName))
			return iter->first;

	HRESULT hr = 0;

	// Load the x-file data
	ID3DXBuffer* adjBuffer		= 0;
	ID3DXBuffer* mtrlBuffer		= 0;
	DWORD			numMaterials	= 0;

	// Create a temporary model structure to hold our data
	tModel temp;
	memset(&temp, 0, sizeof(tModel));

	// Store our filename
	temp.fileName = nameBuffer;

	hr = D3DXLoadMeshFromX(nameBuffer,
											D3DXMESH_MANAGED,
											m_pDevice,
											&adjBuffer,
											&mtrlBuffer,
											0,
											&numMaterials,
											&temp.mesh);

	// Check if we successfully loaded the model
	if (FAILED(hr))
	{
		::MessageBox(0, "D3DXLoadMeshFromX - Failed", 0, 0);
		return -1;
	}
	
	// Extract the materials and load textures
	if(mtrlBuffer != 0 && numMaterials != 0)
	{
	    D3DXMATERIAL* mtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer();
	    
	    for(unsigned int i = 0; i < numMaterials; i++)
	    {
	        mtrls[i].MatD3D.Ambient = mtrls[i].MatD3D.Diffuse;
	        
	        // save the material
	        temp.mtrls.push_back(mtrls[i].MatD3D);
	        
	        // check if that material has a texture associated with it
	        if(mtrls[i].pTextureFilename != 0)
	        {
				// we want the manager to look in the textures directory for textures
				char buffer[56] = {0};
				sprintf_s(buffer, sizeof(buffer), "textures/%s", mtrls[i].pTextureFilename);

	            IDirect3DTexture9* tex = 0;
	            D3DXCreateTextureFromFile(m_pDevice, 
                                          buffer,
                                          &tex);
                
                // save the texture
                temp.textures.push_back(tex);
            }
            else
                temp.textures.push_back(0);
        }
    }
    
    // Does the mesh have a D3DFVF_NORMAL in its vertex format?
    if ( !(temp.mesh->GetFVF() & D3DFVF_NORMAL) )
    {
         // no, so clone a new mesh and add D3DFVF_NORMAL to its format:
         ID3DXMesh* pTempMesh = 0;
         temp.mesh->CloneMeshFVF(
              D3DXMESH_MANAGED,
              temp.mesh->GetFVF() | D3DFVF_NORMAL, // add it here
              m_pDevice,
              &pTempMesh );

         temp.mesh->Release();  // get rid of the old mesh
         temp.mesh = pTempMesh; // save the new mesh
    }

    // Done with our material buffer
	mtrlBuffer->Release();

    // compute the normals:
    D3DXComputeNormals( temp.mesh, 0 );

	// initalize the transform matrix for the primitive
	D3DXMatrixIdentity(&temp.matTransform);
	
	// Just testing the below code for now.
	// Add the mesh to our ModelList
	m_Models[m_Models.size()] = temp;
	
	return m_Models.size() -1;
}

//=====================================================
// Function	:	DrawAllModels()
//
//	Purpose	:	Render all of the models in our collection of models.
//=====================================================
void CModelManager::DrawAllModels(void)
{
    // Simply loop through all the models in our map and call the draw function.
    for(unsigned int i = 0; i < m_Models.size(); i++)
        DrawModel(i);
}

//=====================================================
// Function	:	DrawModel()
//
//	In			:	nID - The ID of the model to render.
//
//	Purpose	:	Render a specific model in our model List.
//=====================================================
bool CModelManager::DrawModel(int nID)
{
    // If the model cant be found in our map throw an error and return.
    if(m_Models.find(nID) == m_Models.end())
    {
        ::MessageBox(0, "DrawModel() - could not find model in map.", 0, 0);
        return false;
    }
    
    // Transform from model space to world space
	m_pDevice->SetTransform(D3DTS_WORLD, &m_Models[nID].matTransform);
    
    // Our mesh was found so we can begin drawing
    for(unsigned int i = 0; i < m_Models[nID].mtrls.size(); i++)
    {
       m_pDevice->SetMaterial( &m_Models[nID].mtrls[i] );
       m_pDevice->SetTexture(0, m_Models[nID].textures[i]);
       m_Models[nID].mesh->DrawSubset(i);
    }

	return true;
}

//=====================================================
// Function	:	CleanUp()
//
//	Purpose	:	Clean up where needed. Release any allocated memory
//=====================================================
void CModelManager::CleanUp(void)
{
    ModelMap::iterator iter = m_Models.begin();
    
    for(iter; iter != m_Models.end(); iter++)
    {
        
    }
}

void CModelManager::ScaleModel(int nID, float x, float y, float z)
{
	 if(m_Models.find(nID) == m_Models.end())
		 return;

	// scale it
	D3DXMatrixScaling(&m_Models[nID].matTransform, x, y, z);
}

void CModelManager::TranslateModel(int nID, float x, float y, float z)
{
	 if(m_Models.find(nID) == m_Models.end())
		 return;

	m_Models[nID].matTransform._41 = x;
	m_Models[nID].matTransform._42 = y;
	m_Models[nID].matTransform._43 = z;

	//D3DXMatrixTranslation(&m_Models[nID].matTransform, x, y, z);
}